package com.crane.oss.config;


import com.crane.common.framework.shiro.*;
import com.crane.oss.properties.SysConfigProperties;
import com.crane.oss.web.support.shiro.ApiAuthenticationFilter;
import com.crane.oss.web.support.shiro.UserNamePassWordRealm;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.servlet.Filter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Configuration
public class ShiroConfig {
//
//    @Autowired
//    private Environment env;

    @Autowired
    private RedisProperties redisProperties;

    @Autowired
    private SysConfigProperties sysConfigProperties;


    @Bean("shiroRedisConnectionFactory")
    public LettuceConnectionFactory shiroRedisConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setDatabase(redisProperties.getDatabase());
        redisStandaloneConfiguration.setHostName(redisProperties.getHost());
        redisStandaloneConfiguration.setPort(redisProperties.getPort());
        redisStandaloneConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
        LettuceClientConfiguration.LettuceClientConfigurationBuilder lettuceClientConfigurationBuilder = LettuceClientConfiguration.builder();
        LettuceConnectionFactory factory = new LettuceConnectionFactory(redisStandaloneConfiguration,
                lettuceClientConfigurationBuilder.build());
        factory.setDatabase(4);
        return factory;
    }


    @Bean("shiroRedisSessionRedisTemplate")
    public RedisTemplate<String, Session> shiroRedisSessionRedisTemplate() {
        RedisTemplate<String, Session> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(shiroRedisConnectionFactory());
        JdkRedisSerializer<Serializable> collectionSerializer = JdkRedisSerializer.getInstance();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(collectionSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }


    @Bean("shiroRedisCacheRedisTemplate")
    public RedisTemplate<Object, Serializable> shiroRedisCacheRedisTemplate() {
        RedisTemplate<Object, Serializable> cacheRedisTemplate = new RedisTemplate<>();
        cacheRedisTemplate.setConnectionFactory(shiroRedisConnectionFactory());
        cacheRedisTemplate.afterPropertiesSet();
        return cacheRedisTemplate;
    }

    @Bean
    public SessionDAO sessionDAO() {
        ShiroRedisSessionDAO sessionDAO = new ShiroRedisSessionDAO();
        sessionDAO.setRedisTemplate(shiroRedisSessionRedisTemplate());
        sessionDAO.setRedisSessionTimeout(24 * 60 * 60 * 1000);
        sessionDAO.setKeyPrefix("dbsoss");
        return sessionDAO;
    }

    @Bean
    public CacheManager cacheManager() {
        ShiroRedisCacheManager shiroRedisCacheManager = new ShiroRedisCacheManager();
        shiroRedisCacheManager.setRedisTemplate(shiroRedisCacheRedisTemplate());
        shiroRedisCacheManager.setExpire(24 * 60 * 60 * 1000);
        shiroRedisCacheManager.setKeyPrefix("dbsoss");
        return shiroRedisCacheManager;
    }

    @Bean
    protected SessionManager sessionManager() {
        HeaderWebSessionManager sessionManager = new HeaderWebSessionManager();
        sessionManager.setSessionDAO(sessionDAO());
        sessionManager.setSessionValidationSchedulerEnabled(false);
        sessionManager.setGlobalSessionTimeout(12 * 60 * 60 * 1000);// 30分钟
        return sessionManager;
    }

    @Bean
    protected SessionsSecurityManager securityManager(List<Realm> realms) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setCacheManager(cacheManager());
        securityManager.setSessionManager(sessionManager());
        securityManager.setRealms(realms);
        return securityManager;
    }

    @Bean
    public AuthorizingRealm userNamePassWordRealm() {
        UserNamePassWordRealm userRealm = new UserNamePassWordRealm();
        userRealm.setAuthenticationCachingEnabled(true);
        userRealm.setAuthorizationCachingEnabled(true);
        return userRealm;
    }

    @Bean
    protected ShiroFilterFactoryBean shiroFilterFactoryBean(List<Realm> realms) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setLoginUrl("/manager/login");
        shiroFilter.setSecurityManager(securityManager(realms));

        Map<String, Filter> filters = new HashMap<>();

        ApiAuthenticationFilter apiAuthenticationFilter = new ApiAuthenticationFilter();
        ApiAuthenticationFilter.setAllowOrigins(sysConfigProperties.getAllowOrigins());

        filters.put("api", apiAuthenticationFilter);

        shiroFilter.setFilters(filters);

        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/statics/**", "anon");
        filterMap.put("/js/**", "anon");
        filterMap.put("/favicon.ico", "anon");
        filterMap.put("/sys/manager/login", "anon");
        filterMap.put("/sys/manager/forgetPassword", "anon");
        filterMap.put("/sys/manager/captcha", "anon");
        filterMap.put("/sys/manager/logout", "anon");
        filterMap.put("/sys/menu/routeList", "anon");


        filterMap.put("/doc.html", "anon");
        filterMap.put("/swagger-ui.html", "anon");
        filterMap.put("/swagger-resources/**", "anon");
        filterMap.put("/image/**", "anon");
        filterMap.put("/v2/api-docs", "anon");
        filterMap.put("/webjars/**", "anon");
        filterMap.put("/**", "anon");
        filterMap.put("/**", "api");

        shiroFilter.setFilterChainDefinitionMap(filterMap);
        return shiroFilter;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }


    @Bean
    public ShiroSwitchInterceptor shiroSwitchInterceptor() {
        ShiroSwitchInterceptor shiroSwitchInterceptor = new ShiroSwitchInterceptor();
        shiroSwitchInterceptor.setFlag(sysConfigProperties.getShiroPermissionFlag());
        return shiroSwitchInterceptor;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(List<Realm> realms) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager(realms));
        advisor.setAdvice(shiroSwitchInterceptor());
        return advisor;
    }

}
